import schedule from 'node-schedule';
import { td_posting_main, td_posting_comment_main } from '../../puppeteer_ads/tasks/index.js';
import puppeteer from 'puppeteer';
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
import puppeteer_extra from 'puppeteer-extra';

// Use Stealth plugin to avoid detection
puppeteer_extra.use(StealthPlugin());

// Store active jobs to manage them
const activeJobs = new Map();
// Store active browser instances
const activeBrowsers = new Map();

/**
 * Check if time is within any of the given time ranges
 * @param {Array<Object>} ranges - Array of time ranges with startTime and endTime
 * @returns {boolean} - Whether current time is within any range
 */
function isWithinTimeRanges(ranges) {
  if (!ranges || !Array.isArray(ranges) || ranges.length === 0) {
    return false;
  }

  const now = new Date();
  const currentHours = now.getHours();
  const currentMinutes = now.getMinutes();
  const currentTimeMinutes = currentHours * 60 + currentMinutes;

  for (const range of ranges) {
    const { startTime, endTime } = range;

    if (!startTime || !endTime) continue;

    // Parse time ranges
    const [startHours, startMinutes] = startTime.split(':').map(Number);
    const [endHours, endMinutes] = endTime.split(':').map(Number);

    const startTimeMinutes = startHours * 60 + startMinutes;
    const endTimeMinutes = endHours * 60 + endMinutes;

    // Handle normal case (start time before end time)
    if (startTimeMinutes <= endTimeMinutes) {
      if (currentTimeMinutes >= startTimeMinutes && currentTimeMinutes <= endTimeMinutes) {
        return true;
      }
    }
    // Handle cross-day case (start time after end time)
    else {
      if (currentTimeMinutes >= startTimeMinutes || currentTimeMinutes <= endTimeMinutes) {
        return true;
      }
    }
  }

  return false;
}

/**
 * Calculate next run time based on task configuration
 * @param {Object} taskConfig - Task configuration with interval and random factors
 * @returns {Date} - Next execution time
 */
function calculateNextRunTime(taskConfig) {
  // Default values if not provided
  const intervalHours = taskConfig.find(item => item.name === 'interval_posting')?.value || 6;
  const maxRandomFactor = taskConfig.find(item => item.name === 'max_count')?.value || 1.2;
  const minRandomFactor = taskConfig.find(item => item.name === 'min_count')?.value || 0.8;

  // Calculate random interval (between min and max factor)
  const randomFactor = Math.random() * (maxRandomFactor - minRandomFactor) + minRandomFactor;
  const intervalMinutes = Math.floor(intervalHours * randomFactor * 60);

  const nextRun = new Date();
  nextRun.setMinutes(nextRun.getMinutes() + intervalMinutes);

  return nextRun;
}

/**
 * Execute a specific task based on its type
 * @param {Object} task - Task object
 * @param {string} taskType - Type of the task
 * @returns {Promise<Object>} - Task execution result
 */
async function executeTask(task, taskType) {
  try {
    console.log(`${new Date().toISOString()} [INFO]: Executing task ${taskType} for account ${task.platformAccount}`);

    // Launch browser if not already launched for this task
    if (!activeBrowsers.has(task.taskId)) {
      const browser = await puppeteer_extra.launch({
        headless: false,
        args: [
          '--no-sandbox',
          '--disable-setuid-sandbox',
          '--disable-infobars',
          '--window-size=1920,1080',
        ],
        defaultViewport: { width: 1920, height: 1080 }
      });

      activeBrowsers.set(task.taskId, browser);
    }

    const browser = activeBrowsers.get(task.taskId);

    // Prepare parameters from task configuration
    const taskParams = {
      account: task.platformAccount,
      // Other parameters would be constructed based on task type
      // and extracted from task.composeData
    };

    let result;

    // Execute task based on type
    switch (taskType) {
      case 'td_posting':
        result = await td_posting_main(taskParams, browser, 0, task.taskId);
        break;
      case 'td_posting_comment':
        result = await td_posting_comment_main(taskParams, browser, 0, task.taskId);
        break;
      default:
        throw new Error(`Unknown task type: ${taskType}`);
    }

    console.log(`${new Date().toISOString()} [INFO]: Task ${taskType} completed with result:`, result);
    return result;

  } catch (error) {
    console.error(`${new Date().toISOString()} [ERROR]: Failed to execute task ${taskType}:`, error);
    return { success: false, message: error.message };
  }
}

/**
 * Schedule a specific task within a task policy
 * @param {Object} task - Complete task policy
 * @param {Object} composeItem - Specific task within the policy
 */
function scheduleTask(task, composeItem) {
  const taskType = composeItem.name;
  const taskConfig = composeItem.config;
  const taskId = `${task.taskId}-${taskType}`;

  // Cancel existing job if present
  if (activeJobs.has(taskId)) {
    activeJobs.get(taskId).cancel();
    activeJobs.delete(taskId);
  }

  // Create a job that checks time ranges and executes task
  const job = schedule.scheduleJob('*/5 * * * *', async function() {
    try {
      // Check if current time is within operation time ranges
      const { ranges, offsetMinutesMin, offsetMinutesMax } = task.operatingTimeRanges || {};

      if (!isWithinTimeRanges(ranges)) {
        console.log(`${new Date().toISOString()} [INFO]: Outside operating hours for task ${taskId}. Skipping execution.`);

        // Close browser if outside operating hours
        if (activeBrowsers.has(task.taskId)) {
          await activeBrowsers.get(task.taskId).close();
          activeBrowsers.delete(task.taskId);
        }

        return;
      }

      // Execute the task
      await executeTask(task, taskType);

      // Schedule next run based on task configuration
      const nextRunTime = calculateNextRunTime(taskConfig);
      console.log(`${new Date().toISOString()} [INFO]: Next run for task ${taskId} scheduled at ${nextRunTime.toISOString()}`);

      // Reschedule with calculated time
      activeJobs.get(taskId).reschedule(nextRunTime);

    } catch (error) {
      console.error(`${new Date().toISOString()} [ERROR]: Error in scheduled job for task ${taskId}:`, error);
    }
  });

  activeJobs.set(taskId, job);
  console.log(`${new Date().toISOString()} [INFO]: Task ${taskId} scheduled`);
}

/**
 * Main function to start task scheduler
 * @param {Array<Object>} tasksOfRuns - Array of task policies
 */
export function startTaskScheduler(tasksOfRuns) {
  if (!tasksOfRuns || !Array.isArray(tasksOfRuns) || tasksOfRuns.length === 0) {
    console.warn(`${new Date().toISOString()} [WARN]: No tasks provided to scheduler`);
    return;
  }

  console.log(`${new Date().toISOString()} [INFO]: Starting task scheduler with ${tasksOfRuns.length} policies`);

  // Stop all existing jobs
  stopAllTasks();

  // Process each task policy
  for (const task of tasksOfRuns) {
    // Schedule each task within the policy
    if (task.composeData && Array.isArray(task.composeData)) {
      for (const composeItem of task.composeData) {
        scheduleTask(task, composeItem);
      }
    }
  }
}

/**
 * Stop all scheduled tasks and clean up resources
 */
export async function stopAllTasks() {
  console.log(`${new Date().toISOString()} [INFO]: Stopping all scheduled tasks`);

  // Cancel all scheduled jobs
  for (const [jobId, job] of activeJobs.entries()) {
    job.cancel();
    console.log(`${new Date().toISOString()} [INFO]: Cancelled job ${jobId}`);
  }
  activeJobs.clear();

  // Close all browser instances
  for (const [taskId, browser] of activeBrowsers.entries()) {
    await browser.close();
    console.log(`${new Date().toISOString()} [INFO]: Closed browser for task ${taskId}`);
  }
  activeBrowsers.clear();
}

/**
 * Get status of all scheduled tasks
 * @returns {Array<Object>} - Status of all tasks
 */
export function getSchedulerStatus() {
  const status = [];

  for (const [jobId, job] of activeJobs.entries()) {
    const [taskId, taskType] = jobId.split('-');

    status.push({
      taskId,
      taskType,
      nextInvocation: job.nextInvocation(),
      running: !!activeBrowsers.get(taskId)
    });
  }

  return status;
}

export default {
  startTaskScheduler,
  stopAllTasks,
  getSchedulerStatus
};